﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Szamlazz.Utils;
using R = Szamlazz.Data.Request;

namespace Szamlazz.Data
{ 
	/// <summary>
	/// Számla tétel
	/// </summary>
	public class InvoiceItem : Validatable
	{
		/// <summary>
		/// Kerekítés módja
		/// </summary>
		public enum RoundingMode
		{
			/// <summary>
			/// Nettó alapú kereítés
			/// </summary>
			NetBased,
			/// <summary>
			/// Bruttó alapú kerekítés
			/// </summary>
			GrossBased
		}

		/// <summary>
		/// Kerekítés módja
		/// </summary>
		public RoundingMode Rounding { get; private set; }

		/// <summary>
		/// Belső konstruktor
		/// </summary>
		/// <param name="mode">kerekítés módja</param>
		internal InvoiceItem(RoundingMode mode)
		{
			this.Rounding = mode;
		}

		/// <summary>
		/// Komponens neve loggoláskor
		/// </summary>
		internal override string Component
		{
			get { return "számla tétel"; }
		}

		/// <summary>
		/// Az értékek kalkulálva vannak-e
		/// </summary>
		public bool IsCalculated { get; private set; }

		private double _NetUnitPrice = 0;
		/// <summary>
		/// Nettó egységár
		/// </summary>
		public double NetUnitPrice
		{ 
			get { return _NetUnitPrice; }
			set 
			{
				if (this.Rounding == RoundingMode.NetBased)
				{
					_NetUnitPrice = value;
					Recalculate();
				}
				else
				{
					throw new InvalidOperationException("A nettó egységár csak nettó alapú kerekítés esetén módosítható!");
				}
			}
		}

		private double _GrossUnitPrice = 0;
		/// <summary>
		/// Bruttó egységár
		/// </summary>
		public double GrossUnitPrice
		{
			get { return _GrossUnitPrice; }
			set
			{
				if (this.Rounding == RoundingMode.GrossBased)
				{
					_GrossUnitPrice = value;
					Recalculate();
				}
				else
				{
					throw new InvalidOperationException("A bruttó egységár csak bruttó alapú kerekítés esetén módosítható!");
				}
			}
		}

		private double _Quantity = 1;
		/// <summary>
		/// Mennyiség
		/// </summary>
		public double Quantity
		{
			get { return _Quantity; }
			set
			{
				_Quantity = value;
				Recalculate();
			}
		}

		private VATType _TaxType = null;
		/// <summary>
		/// Áfa fajtája
		/// </summary>
		public VATType TaxType
		{
			get { return _TaxType; }
			set
			{
				_TaxType = value;
				Recalculate();
			}
		}

		/// <summary>
		/// Számla tétel megnevezése
		/// </summary>
		public string Name { get; set; }
		/// <summary>
		/// Leírása
		/// </summary>
		public string Description { get; set; }
		/// <summary>
		/// Mennyiség egysége
		/// </summary>
		public string Unit { get; set; }

		/// <summary>
		/// Adó kalkulált mértéke
		/// </summary>
		public int VAT { get; private set; }

		/// <summary>
		/// Nettó kalkulált érték
		/// </summary>
		public int NetValue { get; private set; }
		/// <summary>
		/// Bruttó kalkulált érték
		/// </summary>
		public int GrossValue { get; private set; }

		/// <summary>
		/// Kalkulálja az értékeket a megadott értékek függvényében
		/// </summary>
		internal void Calculate()
		{
			if (this.IsCalculated == false)
			{
				this.IsCalculated = true;
				this.Recalculate();
			}
		}

		/// <summary>
		/// Kalkulációs algoritmus, nettó vagy bruttó alapú kalkuláció
		/// </summary>
		private void Recalculate()
		{
			if (this.IsCalculated && this.TaxType != null)
			{
				if (this.Rounding == RoundingMode.NetBased)
				{
					this.NetValue = (int)Math.Round(this.NetUnitPrice * this.Quantity);
					this.VAT = (int)Math.Round((double)this.NetValue * (this.TaxType.Value));
					this.GrossValue = this.NetValue + this.VAT;
				}
				else
				{
					this.GrossValue = (int)Math.Round(this.GrossUnitPrice * this.Quantity);
					this._NetUnitPrice = (int) Math.Round(this.GrossUnitPrice / (1D + this.TaxType.Value));
					this.VAT = (int)Math.Round((double)this.GrossValue * this.TaxType.ReversedValue);
					this.NetValue = this.GrossValue - this.VAT;
				}
			}
		}

		/// <summary>
		/// Létrehozza a kéréshez az XML kommunikációs objektumot
		/// </summary>
		/// <returns></returns>
		internal R.tetelTipus CreateRequest()
		{
			return new R.tetelTipus 
			{ 
				nettoEgysegar = this.NetUnitPrice,
				nettoErtek = this.NetValue,
				bruttoErtek = this.GrossValue,
				afaErtek = this.VAT,
				afakulcs = this.TaxType.Code,
				mennyiseg = this.Quantity,
				mennyisegiEgyseg = this.Unit,
				megnevezes = this.Name,
				megjegyzes = this.Description
			};
		}

		/// <summary>
		/// Ellenőrzi a kötelező mezőket
		/// </summary>
		/// <returns></returns>
		internal override ValidationResult Validate()
		{
			var ret = base.Validate();

			ret.Evaluate(string.IsNullOrEmpty(this.Name), "megnevezés", ValidationResult.ValidationReason.Required);
			ret.Evaluate(this.Quantity == 0, "mennyiség", ValidationResult.ValidationReason.Required);
			ret.Evaluate(this.Rounding == RoundingMode.NetBased ? this.NetUnitPrice <= 0 : this.GrossUnitPrice <= 0, "egységár", ValidationResult.ValidationReason.Required);
			ret.Evaluate(this.TaxType == null, "ÁFA típus", ValidationResult.ValidationReason.Required);

			return ret;
		}
	}
}
